/*
 * discoverAgent.cpp
 *
 *  Created on: 2011-8-17
 *      Author: fufan
 */
#include "discoverAgent.h"

static const char *oids[] =
{
		"1.3.6.1.2.1.1.5.0",   //名称
		"1.3.6.1.2.1.1.4.0",   //联系人
		"1.3.6.1.2.1.1.6.0",   //地址
		"1.3.6.1.2.1.1.3.0" ,  //运行时间
		"1.3.6.1.2.1.1.1.0",   //描述
};

extern int receive_snmp_response(SnmpSocket sock, Snmp &snmp_session, Pdu &pdu,
		UdpAddress &fromaddress, OctetStr &engine_id, bool process_msg = true);

void printError(const QString &error);

DiscoverAgent::DiscoverAgent(Ui::MainWindow *mainw)
{
		agentBox = mainw->agentBox;
		startDiscoverBtn = mainw->startDiscover;
		stopDiscoverBtn = mainw->stopDiscover;
		discoveryProgress = mainw->dicoveryProgress;
		discoveryResult = mainw->discoveryResult;
		discoverThread = new DiscoverThread(mainw->startIp, mainw->endIp);


		startDiscoverBtn->setEnabled(true);
		stopDiscoverBtn->setEnabled(false);
		connect(startDiscoverBtn, SIGNAL(clicked(bool)), this, SLOT(startDiscover()));
		connect(stopDiscoverBtn, SIGNAL(clicked(bool)), this, SLOT(stopDiscover()));
		discoveryProgress->setRange(0, VERSIONS*TIMEOUT);


		QStringList titles;
		titles << "IP地址" << "版本"  <<"名称" << "联系人" << "地址" << "运行时间" <<  "描述";
		discoveryResult->setColumnCount(titles.size());
		discoveryResult->setHorizontalHeaderLabels(titles);
		discoveryResult->setEditTriggers(QAbstractItemView::NoEditTriggers);
		discoveryResult->setSelectionBehavior(QAbstractItemView::SelectRows);
		discoveryResult->horizontalHeader()->setStretchLastSection(true);
		discoveryResult->setContextMenuPolicy(Qt::CustomContextMenu);
		connect(discoveryResult, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(createMenu(const QPoint &)));

		connect(discoverThread, SIGNAL(sendAgentInfo(const QStringList &)), this, SLOT(showAgentInfo(const QStringList &)));
		connect(discoverThread, SIGNAL(sendProgressValue(const int)), this, SLOT(setProgressValue(const int)));
		connect(discoverThread, SIGNAL(signalStartStop(int )), this, SLOT(setStartStop(int )));
}

/*探测指定网段中的代理*/
void DiscoverAgent::startDiscover()
{
		discoveryProgress->reset();
		discoveryResult->clear();
		QStringList titles;
		titles << "IP地址" << "版本"  <<"名称" << "联系人" << "地址" << "运行时间" <<  "描述";
		discoveryResult->setColumnCount(titles.size());
		discoveryResult->setRowCount(0);
		discoveryResult->setHorizontalHeaderLabels(titles);
		discoverThread->start();
}

void DiscoverAgent::stopDiscover()
{
		discoverThread->getSnmp()->isRun = false;
}

void DiscoverAgent::createMenu(const QPoint &point)
{
		//如果列表中没有项则返回
		if(discoveryResult->rowCount() == 0)
		{
			return;
		}

		QPoint pos = discoveryResult->mapToGlobal(point);
		QMenu *menu = new QMenu();

		QAction *addAgent = new QAction("添加为到代理", menu);
		QAction *deleteAgent = new QAction("删除", menu);

		menu->addAction(addAgent);
		menu->addAction(deleteAgent);

		connect(addAgent, SIGNAL(triggered(bool)), this, SLOT(addToAgent()));
		connect(deleteAgent, SIGNAL(triggered(bool)), this, SLOT(deleteAgent()));

		menu->popup(pos);
}

void DiscoverAgent::addToAgent()
{
		//设置默认的值
		QString port = "161";
		QString tries = "3";
		QString timeout = "1";
		QString read_community = "public";
		QString write_community = "private";
		QString non_repeaters = "0";
		QString max_repetitions = "10";
		int row = discoveryResult->currentRow();
		int ipIndex = 0;
		int versionIndex = 1;
		int nameIndex = 2;

		QSettings setting("WhuCS", "QSnmp");
		setting.beginGroup(discoveryResult->item(row, nameIndex)->data(Qt::DisplayRole).toString());
		setting.setValue("ipAddress", discoveryResult->item(row, ipIndex)->data(Qt::DisplayRole).toString());
		setting.setValue("port", port);
		setting.setValue("tries", tries);
		setting.setValue("timeout", timeout);
		setting.setValue("read_community", read_community);
		setting.setValue("write_community", write_community);

		QString version = discoveryResult->item(row, versionIndex)->data(Qt::DisplayRole).toString();

		if(!version.contains("V1"))
		{
					setting.setValue("snmp1", "false");
		}
		else
		{
				setting.setValue("snmp1", "true");
		}
		if(!version.contains("V2c"))
		{
					setting.setValue("snmp2", "false");
		}
		else
		{
				setting.setValue("snmp2", "true");
		}
		setting.setValue("non_repeaters", non_repeaters);
		setting.setValue("max_repetitions", max_repetitions);
		setting.endGroup();
		//将项添加到ComboBox中
		agentBox->addItem(discoveryResult->item(row, nameIndex)->data(Qt::DisplayRole).toString());
}

void DiscoverAgent::deleteAgent()
{
		int row = discoveryResult->currentRow();
		discoveryResult->removeRow(row);
}

void DiscoverAgent::showAgentInfo(const QStringList &agentInfo)
{
		//index表示IP地址在一行记录中的位置
		int ipIndex = 0;
		int versionIndex = 1;
		QString ipAddr = agentInfo[ipIndex];

		bool flag = true;
		for(int i=0; i<discoveryResult->rowCount(); i++)
		{
			if(discoveryResult->item(i, ipIndex)->data(Qt::DisplayRole).toString() == ipAddr)
			{
				QString versions = discoveryResult->item(i,versionIndex)->data(Qt::DisplayRole).toString();
				versions += "/";
				versions += agentInfo[versionIndex];
				QTableWidgetItem *item = new QTableWidgetItem(versions);
				discoveryResult->setItem(i, versionIndex, item);
				flag = false;
			}
		}

	if(flag)
	{
		int rowCount = discoveryResult->rowCount();
		discoveryResult->setRowCount(rowCount+1);
		for(int i=0; i<agentInfo.count(); i++)
		{
					QTableWidgetItem *item = new QTableWidgetItem(agentInfo[i]);
					discoveryResult->setItem(rowCount, i, item);
		}

	}
}

void DiscoverAgent::setProgressValue(const int value)
{
		discoveryProgress->setValue(value);
}

void DiscoverAgent::setStartStop(int flag)
{
		if(flag)
		{
				startDiscoverBtn->setEnabled(true);
				stopDiscoverBtn->setEnabled(false);
		}
		else
		{
				startDiscoverBtn->setEnabled(false);
				stopDiscoverBtn->setEnabled(true);
		}
}


DiscoverThread::DiscoverThread(QLineEdit *startIp, QLineEdit *endIp)
{
		this->progressValue = 0;
		this->startIp = startIp;
		this->endIp = endIp;

		int status;
		discoverSnmp = new DiscoverSnmp(status);
		discoverSnmp->isRun = false;
		if(status != SNMP_CLASS_SUCCESS)
		{
			printError("创建会话失败！");
		}

}

void DiscoverThread::run()
{
	progressValue = 0;
	emit signalStartStop(0);
	getSnmp()->isRun = true;
	snmp_version versions[2] = {version1, version2c};
	for(int i=0; i<2; i++)
	{
		discoverSnmp->discover(startIp->text(), endIp->text(), READCOMMUNITY, versions[i], TIMEOUT, this);
		if(!discoverSnmp->isRun)
			break;
	}
	emit signalStartStop(1);
}

void DiscoverThread::progress()
{
		emit sendProgressValue(++progressValue);
}

void DiscoverThread::getAgentInfo(const Pdu &pdu,const UdpAddress &address, const snmp_version version)
{
			QStringList agentInfo;

			QString host = address.get_printable();
			QString ipAddr = host.left(host.indexOf(QChar('/')));

			agentInfo << ipAddr;

			switch(version)
			{
			case version1:
				agentInfo << QString("V1");
				break;
			case version2c:
				agentInfo << QString("V2c");
				break;
			default:
				break;
			}

			for(int i=0; i<pdu.get_vb_count(); i++)
			{
				agentInfo << QString(pdu.get_vb(i).get_printable_value());
			}

			emit sendAgentInfo(agentInfo);
}

DiscoverSnmp* DiscoverThread::getSnmp()
{
			return discoverSnmp;
}

void DiscoverSnmp::discover(const QString &startIp, const QString &endIp, const OctetStr &read_community,
		const snmp_version &version, int timeoutSec, DiscoverThread *thread)
{
			SnmpMessage *snmpMsg = NULL;
			int msgLength = 0;
			unsigned char *msg = NULL;
			unsigned int sock = iv_snmp_session;

			int sLen = startIp.length() - startIp.lastIndexOf(QChar('.')) - 1;
			int eLen = endIp.length() - endIp.lastIndexOf(QChar('.')) - 1;
			int start = startIp.right(sLen).toInt();
			int end = endIp.right(eLen).toInt();
			if(start > end)
			{
				printError("起始地址应小于结束地址！");
				return;
			}

			//startIp和endIp只有最后一段数字不同
			QString headIp = startIp.left(startIp.lastIndexOf(QChar('.'))+1);
			//数据部分为5个oid实例
			Pdu pdu;
			for(int i=0; i<5; i++)
			{
				Vb vb;
				vb.set_oid(oids[i]);
				pdu += vb;
			}
			pdu.set_error_index(0);
			pdu.set_type(sNMP_PDU_GET);

			//构造get报文，如果使用get函数需要设置retry和timeout与本函数的目的不一致
			for(int i=start; i<=end; i++)
			{
				//检查是否终止探测
				if(isRun == false)
				{
					if(!snmpMsg)
						delete snmpMsg;
					return;
				}
				QString tailIp = QString("%1").arg(i);
				QString ip = headIp + tailIp;

				UdpAddress udpAddress(ip.toStdString().c_str());

				if(iv_snmp_session != (int)INVALID_SOCKET && udpAddress.get_ip_version() == Address::version_ipv4)
				{
									sock = iv_snmp_session;
				}

				snmpMsg = new SnmpMessage();
				pdu.set_request_id(MyMakeReqId());
				if(snmpMsg->load(pdu, read_community, version) != SNMP_CLASS_SUCCESS)
				{
						if(!snmpMsg)
							delete snmpMsg;
						continue;

				}
				msgLength = snmpMsg->len();
				msg = snmpMsg->data();

				struct sockaddr_in toAddr;
				memset(&toAddr, 0, sizeof(toAddr));

				toAddr.sin_family = AF_INET;
				toAddr.sin_addr.s_addr = inet_addr(ip.toStdString().c_str());
				toAddr.sin_port = htons(PORT);

				int result;
				if((result=(sendto(iv_snmp_session, (char *)msg, msgLength, 0, (struct sockaddr *) &toAddr, sizeof(toAddr)))) < 0)
				{
					if(!snmpMsg)
								delete snmpMsg;
					qDebug(strerror(result));
					return;

				}
			/*if(send_raw_data(msg, msgLength, udpAddress, iv_snmp_session) < 0)
				{
						if(!snmpMsg)
							delete snmpMsg;
						return;
				}*/
				if(!snmpMsg)
					delete snmpMsg;
			}

			//接收响应
			Pdu recvPdu;
			fd_set readfds;
			int recv = 0;
			int step = 1;
			struct timeval timeout;
			msec mtime;
			mtime += 1000;
			mtime.GetDeltaFromNow(timeout);
			while(timeout.tv_sec != 0 || timeout.tv_usec != 0)
			{
					//检查是否终止探测
					if(isRun == false)
					{
							return;
					}
					FD_ZERO(&readfds);
					FD_SET(sock, &readfds);
					//收到报文
					if((recv=select(int(sock+1), &readfds, NULL, NULL, &timeout)) > 0 && FD_ISSET(sock, &readfds))
					{
						UdpAddress from;
						OctetStr engine_id;
						if(receive_snmp_response(sock, *this, recvPdu, from, engine_id) ==  SNMP_CLASS_SUCCESS)
						{
							thread->getAgentInfo(recvPdu, from, version);
						}
						else
						{
								qDebug("接收响应失败！");
						}

					}
					//以1秒为单位增加进度条
					if(timeout.tv_sec == 0 && timeout.tv_usec == 0)
					{
						thread->progress();
						if(timeoutSec > step)
						{
							step += 1;
							mtime.refresh();
							mtime += 1000;
							mtime.GetDeltaFromNow(timeout);
						}
					}
			}
}



void printError(const QString &error)
{
		QMessageBox msg;
		msg.setWindowIconText("./images/QSnmp.ico");
		msg.setText(error);
		msg.exec();
}
